<template>
	<view class="w-select" id="wSelect" :style="{
      '--select-wrap-width': width,
      '--select-wrap-height': height,
      '--select-bg-color': bgColor
    }">
		<view :class="isShow ? 'select-wrap-active' : ''" class="select-wrap" @click="changeShow">
			<view v-if="multiple" class="select-content">
				<view class="select-content-item-default" v-if="multiSelectList.length === 0 && !filterable">
					{{ placeholder }}
				</view>
				<view class="content-item" v-if="showAll">
					<view class="select-content-item" v-for="(item,index) in multiSelectList" :key="index">
						{{ item[valueName] }}
					</view>
				</view>
				<view class="content-item" v-else>
					<view class="select-content-item" v-if="multiSelectList.length > 0">
						{{ multiSelectList[0][valueName] }}
					</view>
					<view class="select-content-item" v-if="multiSelectList.length > 1">
						{{ multiLength }}
					</view>
				</view>
			</view>
			<input v-if="!multiple || filterable" type="text" @input="inputChange" @blur="blurChange"
				:placeholder="multiple ? multiSelectList.length === 0 ? placeholder : '' : placeholder"
				:disabled="!filterable" :style="!filterable ? 'pointer-events: none' : ''" :value="inputData">
			<!-- #ifdef VUE2 -->
			<view @click.stop="refreshValue" class="close-icon"
				v-if="showClose && (multiple ? value.length > 0 : value)">
				<image :src="refreshUrl" mode="" />
			</view>
			<view v-if="value.length <= 0 || !showClose" :class="isShow ? 'w-select-arrow-up' : ''"
				class="w-select-arrow " />
			<!-- #endif -->
			<!-- #ifdef VUE3 -->
			<view @click.stop="refreshValue" class="close-icon"
				v-if="showClose && (multiple ? modelValue.length > 0 : modelValue)">
				<image :src="refreshUrl" mode="" />
			</view>
			<view v-if="modelValue.length <= 0 || !showClose" :class="isShow ? 'w-select-arrow-up' : ''"
				class="w-select-arrow " />
			<!-- #endif -->

			<scroll-view scroll-y v-show="optionsShow" :class="[
          isShow
            ? showPosition === 'bottom'
              ? 'animation-bottom-in'
              : 'animation-top-in'
            : showPosition === 'bottom'
              ? 'animation-bottom-out'
              : 'animation-top-out',
          showPosition === 'bottom'
            ? 'position-bottom'
            : 'position-top'
        ]" class="select-options">
				<!-- #ifdef VUE2 -->
				<view @click.stop="handleClickItem(item)" :class="
            multiple &&
              multiSelectList.find(
                res => res[keyName] === item[keyName]
              )
              ? 'item-active'
              : value === item[keyName]
                ? 'item-active'
                : ''
          " v-for="item in filterList" :key="item[keyName]" class="select-option-item">
					{{ item[valueName] }}
				</view>
				<!-- #endif -->
				<!-- #ifdef VUE3 -->
				<view @click.stop="handleClickItem(item)" :class="
            multiple &&
              multiSelectList.find(
                res => res[keyName] === item[keyName]
              )
              ? 'item-active'
              : modelValue === item[keyName]
                ? 'item-active'
                : ''
          " v-for="item in filterList" :key="item[keyName]" class="select-option-item">
					{{ item[valueName] }}
				</view>
				<!-- #endif -->

				<view class="options-no-data" v-if="filterList.length < 1">
					无匹配数据~
				</view>
			</scroll-view>
		</view>
		<view v-if="isShow" @click="closeContentSelect" class="contentMask" />
	</view>
</template>

<script>
	export default {
		props: {
			width: {
				type: String,
				default: '200px'
			},
			height: {
				type: String,
				default: '30px'
			},
			bgColor: {
				type: String,
				default: '#fff'
			},
			// 是否多选
			multiple: {
				type: Boolean,
				default: false
			},
			// 是否可搜索
			filterable: {
				type: Boolean,
				default: false
			},
			// 是否显示全部选中数据
			showAll: {
				type: Boolean,
				default: false
			},
			// 是否显示关闭按钮
			showClose: {
				type: Boolean,
				default: false
			},
			// 渲染列表
			list: {
				type: Array,
				default: () => []
			},
			// #ifdef VUE3
			// 双向绑定的值
			modelValue: {
				type: [Array, String, Number],
				default: ''
			},
			// #endif
			// #ifdef VUE2
			// 双向绑定的值
			value: {
				type: [Array, String, Number],
				default: ''
			},
			// #endif
			// 默认显示的内容
			placeholder: {
				type: String,
				default: '请选择'
			},
			// 显示的内容
			valueName: {
				type: String,
				default: 'label'
			},
			// 绑定的内容
			keyName: {
				type: String,
				default: 'value'
			}
		},
		// #ifdef VUE3
		emits: ['update:modelValue', 'change'],
		// #endif
		watch: {
			list: {
				immediate: true,
				deep: true,
				handler(news) {
					this.filterList = news
					const findItem = news.find(item => {
						let isItem = ''
						// #ifdef VUE3
						if (item[this.keyName] === this.modelValue) {
							isItem = true
						} else {
							isItem = false
						}
						// #endif

						// #ifdef VUE2
						if (item[this.keyName] === this.value) {
							isItem = true
						} else {
							isItem = false
						}
						// #endif
						return isItem
					})
					if (findItem) {
						this.inputData = findItem[this.valueName]
					}
				}
			},
			// #ifdef VUE2
			value: {
				handler(newValue) {
					if (this.multiple) {
						this.multiSelectList = newValue || []
					} else {
						const findItem = this.list.find(it => it[this.keyName] === newValue)
						if (findItem) {
							this.inputData = findItem[this.valueName]
						}
					}
				}
			},
			// #endif
			// #ifdef VUE3
			modelValue: {
				handler(newValue) {
					if (this.multiple) {
						this.multiSelectList = newValue || []
					} else {
						const findItem = this.list.find(it => it[this.keyName] === newValue)
						if (findItem) {
							this.inputData = findItem[this.valueName]
						}
					}
				}
			}
			// #endif
		},
		computed: {
			multiLength() {
				const length = this.multiSelectList.length - 1
				return '+' + length
			},
			bottomDistance() {
				return (
					this.windowHeight - this.distanceTop - this.curHeight
				) // 当前元素距离可视屏幕底部的距离
			}
		},
		data() {
			return {
				inputData: '',
				multiSelectList: [],
				isShow: false,
				optionsShow: false,
				windowHeight: null,
				curHeight: null,
				distanceTop: null,
				showPosition: 'bottom',
				filterList: [],
				refreshUrl: ''
			}
		},
		mounted() {
			this.$nextTick(() => {
				const res = uni.getSystemInfoSync()
				this.windowHeight = res.windowHeight // 当前设备屏幕高度
				uni
					.createSelectorQuery()
					.in(this)
					.select('#wSelect')
					.boundingClientRect(data => {
						this.distanceTop = data.top // 当前元素距离顶部的距离
						this.curHeight = data.height
					})
					.exec()
			})
		},
		methods: {
			showPositon() {
				this.showPosition = 'bottom'
				if (this.bottomDistance < this.windowHeight / 3) {
					this.showPosition = 'top'
				}
			},
			changeShow() {
				this.isShow = !this.isShow
				if (this.isShow === false) {
					this.filterList = this.list
					setTimeout(() => {
						this.optionsShow = false
					}, 200)
				} else {
					this.showPositon()
					this.optionsShow = this.isShow
				}
			},
			closeContentSelect() {
				this.isShow = false
				setTimeout(() => {
					this.optionsShow = false
				}, 200)
			},
			setValue(value = '') {
				// #ifdef VUE3
				this.$emit('update:modelValue', value)
				// #endif

				// #ifdef VUE2
				this.$emit('input', value)
				// #endif
			},
			inputChange(e) {
				const value = e.detail.value
				if (this.multiple && this.filterable) {
					this.inputData = value
				} else {
					this.setValue(value)
					this.inputData = value
				}

				this.filterList = this.list.filter(item =>
					item[this.valueName].includes(value)
				)
			},
			blurChange(e) {
				const value = e.detail.value

				if (this.multiple && this.filterable && value) {
					const curValue = {
						[this.keyName]: value,
						[this.valueName]: value
					}
					this.multiSelect(curValue)
				}
			},
			refreshValue() {
				this.setValue('')
				this.inputData = ''
				this.$emit('change', '')
				this.filterList = this.list
				if (this.multiple) {
					this.multiSelectList = []
				}
			},
			handleClickItem(e) {
				if (this.multiple) {
					this.multiSelect(e)
				} else {
					this.setValue(e[this.keyName])
					this.inputData = e[this.valueName]
					this.$emit('change', e)
					this.changeShow()
				}
			},
			multiSelect(item) {
				const index = this.multiSelectList.findIndex(
					res => res[this.valueName] === item[this.valueName]
				)
				if (index > -1) {
					this.multiSelectList.splice(index, 1)
				} else {
					this.multiSelectList.push(item)
				}
				this.inputData = ''
				this.filterList = this.list
				this.setValue(this.multiSelectList)
				this.$emit('change', item)
			}
		}
	}
</script>
<style lang="scss" scoped>
	.w-select {
		--select-wrap-width: 200px;
		--select-wrap-height: 30px;
		--select-border-radius: 4px;
		--select-border: 1px solid #dcdfe6;
		--select-active-border: 1px solid #409eff;
		--select-options-max-height: 150px;
		--select-option-item-font-size: 14px;
		--select-input-font-size: 14px;
		--no-data-default-color: #999999;
		--select-options-box-shadow: 0px 0px 12px rgb(0 0 0 / 12%);
		--select-bg-color: #ffffff;

		.select-wrap {
			position: relative;
			display: flex;
			justify-content: space-between;
			align-items: center;
			width: var(--select-wrap-width);
			height: var(--select-wrap-height);
			border: var(--select-border);
			border-radius: var(--select-border-radius);
			background-color: var(--select-bg-color);
			transition: all 0.2s;

			input {
				padding: 0 2px;
				width: 100%;
				min-width: 0;
				height: 100%;
				font-size: var(--select-input-font-size);
				flex: 1;
			}

			.select-content {
				display: flex;
				align-items: center;
				font-size: var(--select-option-item-font-size);

				.content-item {
					display: flex;

					.select-content-item {
						margin-left: 5px;
						padding: 2px 6px;
						border-radius: var(--select-border-radius);
						color: #aa93b1;
						background-color: #f4f4f5;
					}
				}

				.select-content-item-default {
					margin-left: 5px;
					color: var(--no-data-default-color);
				}
			}

			.close-icon {
				position: absolute;
				top: 50%;
				right: 7px;
				z-index: 1000;
				width: 15px;
				height: 15px;
				transform: translateY(-50%);

				image {
					width: 100%;
					height: 100%;
				}
			}

			.position-bottom {
				top: calc(var(--select-wrap-height) + 10px);
			}

			.position-top {
				bottom: calc(var(--select-wrap-height) + 10px);
			}

			.select-options {
				position: absolute;
				right: 0;
				left: 0;
				z-index: 999;
				overflow: scroll;
				padding: 10px;
				max-height: var(--select-options-max-height);
				border-radius: var(--select-border-radius);
				background-color: var(--select-bg-color);
				box-shadow: var(--select-options-box-shadow);

				.select-option-item {
					margin-bottom: 5px;
					padding: 5px;
					font-size: var(--select-option-item-font-size);
					transition: background-color 0.2s;
				}

				.item-active {
					font-weight: 700;
					color: #409eff;
					background-color: #f5f7fa;
				}

				.options-no-data {
					font-size: var(--select-option-item-font-size);
					text-align: center;
					color: var(--no-data-default-color);
				}
			}

			.w-select-arrow {
				display: inline-block;
				margin: 3px 10px 0;
				width: 8px;
				height: 8px;
				border-top: 1px solid transparent;
				border-right: 1px solid transparent;
				border-bottom: 1px solid #999999;
				border-left: 1px solid #999999;
				transition: all 0.3s;
				transform: translateY(-50%) rotate(-45deg);
			}

			.w-select-arrow-up {
				transform: rotate(-225deg);
			}
		}

		.select-wrap-active {
			border: var(--select-active-border);
		}

		.animation-bottom-in {
			animation-name: bottom-in;
			animation-duration: 0.4s;
			animation-timing-function: ease-out;
			animation-fill-mode: both;
		}

		.animation-bottom-out {
			animation-name: bottom-out;
			animation-duration: 0.2s;
			animation-timing-function: ease-out;
			animation-fill-mode: both;
		}

		.animation-top-in {
			animation-name: top-in;
			animation-duration: 0.4s;
			animation-timing-function: ease-out;
			animation-fill-mode: both;
		}

		.animation-top-out {
			animation-name: top-out;
			animation-duration: 0.2s;
			animation-timing-function: ease-out;
			animation-fill-mode: both;
		}

		@keyframes bottom-in {
			0% {
				opacity: 0;
				transform: translateY(-15%);
			}

			100% {
				opacity: 1;
				transform: translateY(0);
			}
		}

		@keyframes bottom-out {
			0% {
				opacity: 1;
				transform: translateY(0);
			}

			100% {
				opacity: 0;
				transform: translateY(-20%);
			}
		}

		@keyframes top-in {
			0% {
				opacity: 0;
				transform: translateY(15%);
			}

			100% {
				opacity: 1;
				transform: translateY(0);
			}
		}

		@keyframes top-out {
			0% {
				opacity: 1;
				transform: translateY(0);
			}

			100% {
				opacity: 0;
				transform: translateY(20%);
			}
		}

		.contentMask {
			position: fixed;
			top: 0;
			right: 0;
			bottom: 0;
			left: 0;
			z-index: 998;
			width: 100%;
			height: 100%;
		}
	}
</style>